package com.lzh.persevere.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lzh.persevere.common.R;
import com.lzh.persevere.common.ResultUtil;
import com.lzh.persevere.config.constant.RedisConstants;
import com.lzh.persevere.config.constant.SysConstants;
import com.lzh.persevere.config.constant.SystemInfoConstants;
import com.lzh.persevere.config.enums.request.AppHttpCodeEnum;
import com.lzh.persevere.config.enums.request.ErrorCodeEnum;
import com.lzh.persevere.exception.LzhException;
import com.lzh.persevere.mapper.FileInfoMapper;
import com.lzh.persevere.mapper.SystemInfoMapper;
import com.lzh.persevere.mapper.UserMapper;
import com.lzh.persevere.mapper.UserRoleMapper;
import com.lzh.persevere.model.dto.user.LoginUserDto;
import com.lzh.persevere.model.dto.user.UpdateUserPasswordDto;
import com.lzh.persevere.model.dto.user.UserRegisterDto;
import com.lzh.persevere.model.entity.User;
import com.lzh.persevere.model.entity.UserRole;
import com.lzh.persevere.model.qo.user.UserPageQO;
import com.lzh.persevere.model.ro.user.AddUserRO;
import com.lzh.persevere.model.ro.user.RegisterEmailRO;
import com.lzh.persevere.model.ro.user.UpdatePasswordRO;
import com.lzh.persevere.model.vo.user.LoginUserVo;
import com.lzh.persevere.security.LoginUser;
import com.lzh.persevere.service.UserService;
import com.lzh.persevere.template.PageAbstractTemplate;
import com.lzh.persevere.utils.BeanCopyUtils;
import com.lzh.persevere.utils.JwtUtil;
import com.lzh.persevere.utils.RedisCache;
import com.lzh.persevere.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * 用户表(User)表服务实现类
 *
 * @author luzhiheng
 * @since 2022-09-26
 */
@Slf4j
@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Value("${spring.mail.username}")
    private String fromEmail;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private RedisCache redisCache;

    @Resource
    private JavaMailSender javaMailSender;

    @Resource
    private FileInfoMapper fileInfoMapper;

    @Resource
    private RedisCacheService redisCacheService;

    @Resource
    private EmailService emailService;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private SystemInfoMapper systemInfoMapper;

    @Override
    public R<Map<String, Object>> login(LoginUserDto dto) {
        //用户名不能为空
        if (!StringUtils.hasText(dto.getUserName())) {
            throw new LzhException(AppHttpCodeEnum.REQUIRE_USERNAME);
        }
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(dto.getUserName(), dto.getPassword());

        Authentication authenticate = null;
        try {
            authenticate = authenticationManager.authenticate(authenticationToken);
        } catch (AuthenticationException e) {
            throw new LzhException(ErrorCodeEnum.USERNAME_PASSWORD_ERROR);
        }

        if (Objects.isNull(authenticate)) {
            throw new RuntimeException("用户名或密码错误");
        }

        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String token = JwtUtil.createJWT(userId);

        redisCache.setCacheObject(SysConstants.PRE_LOGIN_USER_REDIS + userId, loginUser, 12, TimeUnit.HOURS);

        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);

        User userInfo = loginUser.getUser();
        userInfo.setPassword("");

        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("userInfo", userInfo);

        return ResultUtil.success(map);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean finishRegister(UserRegisterDto dto) {
        boolean isRegisted = isRegisted(dto.getEmail());
        if (isRegisted) {
            throw new LzhException(501, "邮箱已注册");
        }
        User user = baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUserName, dto.getUserName()));
        if (user != null) {
            throw new LzhException(501, "用户名已存在");
        }
        //验证码校验
        String redisEmailCode = redisCacheService.getRegisterCode(dto.getEmail());
        if (!StringUtils.hasText(redisEmailCode)) {
            throw new LzhException(309, "验证码失效");
        }
        if (!redisEmailCode.equals(dto.getCode())) {
            throw new LzhException(310, "验证码输入错误");
        }
        //保存用户信息
        user = BeanCopyUtils.copyBean(dto, User.class);
        user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));
        user.setAvatar("http://118.89.125.143:9000/persevere-lzh/2025-07/12/默认头像.png");
        save(user);
        // 保存用户角色
        userRoleMapper.insert(new UserRole().setUserId(user.getId()).setRoleId(Long.valueOf(systemInfoMapper.selectByType(SystemInfoConstants.register_user_default_role))));
        return true;
    }

    @Override
    public R<Object> logout() {
        Long userId = SecurityUtils.getUserId();
        if (userId == null) {
            throw new LzhException(AppHttpCodeEnum.USER_NOT_LOGIN);
        }
        redisCache.deleteObject(SysConstants.PRE_LOGIN_USER_REDIS + userId);
        SecurityContextHolder.getContext().setAuthentication(null);
        return ResultUtil.success(null);
    }

    @Override
    public User getUserById(Long userId) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getId, userId);
        User user = getOne(queryWrapper);
        user.setPassword(null);
        return user;
    }

    @Override
    public Boolean updateUserAvatar(String avatar) {
        User user = baseMapper.selectById(SecurityUtils.getUserId());
        if (user == null) {
            return false;
        }
        user.setAvatar(avatar);
        redisCache.deleteObject(RedisConstants.USER_ID_AVATAR_MAP);
        return baseMapper.updateById(user) > 0;
    }

    @Override
    public R updateUserPassword(UpdateUserPasswordDto dto) {
        User user = getById(dto.getUserId());
        //判断用户是否存在
        if (Objects.isNull(user)) {
            return ResultUtil.error(AppHttpCodeEnum.USER_NOT_EXIT);
        }
        //校验当前密码
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

        if (!encoder.matches(dto.getCurPassword(), user.getPassword())) {
            return ResultUtil.error(AppHttpCodeEnum.CUR_PASSWORD_ERROR);
        }
        //判断两次密码输入是否正确
        if (!dto.getNewPassword().equals(dto.getConPassword())) {
            return ResultUtil.error(AppHttpCodeEnum.NEW_CON_PASSWORD_NOT_MATCH);
        }

        //校验修改的密码是否为原密码
        if (encoder.matches(dto.getNewPassword(), user.getPassword())) {
            return ResultUtil.error(AppHttpCodeEnum.NEW_PASSWORD_REPEAT);
        }

        //更新密码
        String newPwdEncode = encoder.encode(dto.getNewPassword());
        user.setPassword(newPwdEncode);
        updateById(user);
        return ResultUtil.success(null);
    }

    @Override
    public Boolean updateUserPassword(UpdatePasswordRO ro) {
        User user = getById(SecurityUtils.getUserId());
        //判断用户是否存在
        if (Objects.isNull(user)) {
            return false;
        }
        //判断两次密码输入是否正确
        if (!ro.getPassword().equals(ro.getConfirmPassword())) {
            return false;
        }
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String newPwdEncode = encoder.encode(ro.getConfirmPassword());
        user.setPassword(newPwdEncode);
        int i = baseMapper.updateById(user);
        return i > 0;
    }

    @Override
    public R rePassword(String email, String newPassword, String conPassword) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getEmail, email);
        User user = getOne(queryWrapper);
        if (Objects.isNull(user)) {
            return ResultUtil.error(AppHttpCodeEnum.USER_NOT_EXIT);
        }

        if (!newPassword.equals(conPassword)) {
            return ResultUtil.error(AppHttpCodeEnum.NEW_CON_PASSWORD_NOT_MATCH);
        }

        LambdaQueryWrapper<User> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(User::getEmail, email);

        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode(conPassword);
        User user1 = new User();
        user1.setPassword(encode);

        update(user1, queryWrapper1);
        return ResultUtil.success(null);
    }

    @Override
    public R<Object> cancelAccount(Long userId) {
        User user = getById(userId);
        if (Objects.isNull(user)) {
            return ResultUtil.error(ErrorCodeEnum.NOT_FOUND_ERROR);
        }
        removeById(userId);
        return ResultUtil.success(null);
    }

    @Override
    public User getUserByUserName(String userName) {
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getUserName, userName);
        userLambdaQueryWrapper.eq(User::getStatus, 1);
        return getOne(userLambdaQueryWrapper);
    }

    @Override
    public String getUserNameByUserId(Long createBy) {
        User user = getById(createBy);
        return Objects.isNull(user) ? "暂无作者" : user.getUserName();
    }

    @Override
    public List<User> listUserByName(String commenter) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(User::getUserName, commenter);
        return list(queryWrapper);
    }

    @Override
    public LoginUserVo getLoginUser() {
        // 先判断是否已登录
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (loginUser == null || loginUser.getUser() == null) {
            throw new LzhException(ErrorCodeEnum.NOT_LOGIN_ERROR);
        }
        User user = baseMapper.selectById(loginUser.getUser().getId());
        if (user == null) {
            throw new LzhException(ErrorCodeEnum.NOT_LOGIN_ERROR);
        }
        LoginUserVo loginUserVo = BeanCopyUtils.copyBean(user, LoginUserVo.class);
        if (CollectionUtil.isNotEmpty(loginUser.getRoles())) {
            loginUserVo.setUserRole(loginUser.getRoles().get(0));
        }
        return loginUserVo;
    }

    @Override
    public LoginUserVo getLoginUserVo(User user) {
        if (user == null) {
            return null;
        }
        LoginUserVo loginUserVO = new LoginUserVo();
        BeanUtils.copyProperties(user, loginUserVO);
        return loginUserVO;
    }

    @Override
    public Map<String, Object> pageList(UserPageQO qo) {
        Map<String, Object> otherQueryMap = new HashMap<>();
        PageAbstractTemplate template = new PageAbstractTemplate() {
            @Override
            public Integer countTotal() {
                return baseMapper.countList(getQueryMap(qo.getPageModel(), otherQueryMap));
            }

            @Override
            public List<Map<String, Object>> queryPageList() {
                List<Map<String, Object>> pageList = baseMapper.getPageList(queryMap);
                Map<String, String> allUserIdNickNameMap = redisCacheService.getAllUserIdNickNameMap();
                if (CollectionUtil.isNotEmpty(pageList)) {
                    pageList.forEach(map -> {
                        map.put("createNickName", allUserIdNickNameMap.get(String.valueOf(map.get("createUserId"))));
                        map.put("updateNickName", allUserIdNickNameMap.get(String.valueOf(map.get("updateUserId"))));
                    });
                }
                return pageList;
            }
        };
        return template.queryPage();
    }

    @Override
    public Boolean addUser(AddUserRO ro) {
        User user = BeanUtil.copyProperties(ro, User.class);

        return true;
    }

    @Override
    public Boolean sendEmailRegisterCode(RegisterEmailRO ro) {
        boolean isRegisted = isRegisted(ro.getEmail());
        if (isRegisted) {
            throw new LzhException(502, "邮箱已注册");
        }
        User user = baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUserName, ro.getUserName()));
        if (user != null) {
            throw new LzhException(503, "用户名已存在");
        }
        String registerCode = redisCacheService.getRegisterCode(ro.getEmail());
        if (StrUtil.isNotEmpty(registerCode)) {
            throw new LzhException(501, "验证码已发送");
        }
        String subject = "【zhiheng.lu】注册验证码";
        String code = redisCacheService.generateRegisterCode(ro.getEmail());
        return emailService.sendRegisterEmail(ro.getEmail(), subject, code);
    }

    private String sendCode(String email) {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setFrom(fromEmail);
        mailMessage.setTo(email);
        mailMessage.setSubject("PERSEVERE BLOG");
        int sendEmailCode = (int) (Math.random() * ((999999 - 100000 + 1) + 100000));
        String context = "注册验证码为: " + sendEmailCode + ",一分钟内有效，请妥善保管!";
        mailMessage.setText(context);
        javaMailSender.send(mailMessage);
        return sendEmailCode + "";
    }

    private boolean isRegisted(String email) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getEmail, email);
        User user = getOne(queryWrapper);
        if (Objects.isNull(user)) {
            return false;
        }
        return true;
    }

    private String sendEmailCode(String email, String subject, String preContext) {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setFrom(fromEmail);
        mailMessage.setTo(email);
        mailMessage.setSubject(subject);
        int sendEmailCode = (int) (Math.random() * ((999999 - 100000 + 1) + 100000));
        log.info("发送邮箱验证码-----------" + sendEmailCode);
        String context = preContext + sendEmailCode + ",一分钟内有效，请妥善保管!";
        mailMessage.setText(context);
        javaMailSender.send(mailMessage);

        return sendEmailCode + "";
    }

}

